#include "pdeDoseModel.h"
#include "bson_helper.h"
#include <fstream>

namespace pde {

	DoseModel::DoseModel() {

	}
	DoseModel::~DoseModel() {

	}
	std::shared_ptr<DoseModel> DoseModel::New() {
		return std::make_shared<DoseModel>();
	}

	std::string  DoseModel::to_bson() {
		auto doc = document{};
		to_bson_value("energy", energy_);
		to_bson_value("spot_tune_id", spot_tune_id_);
		to_bson_value("ions_per_mu", ions_per_mu_);
		to_bson_value("idd_scale_factor", idd_scale_factor_);
		to_bson_value("bragg_peak_wel", bragg_peak_wel_);
		to_bson_value("energy_spread", energy_spread_);

		to_bson_vector("depth", depth_);
		to_bson_vector("dose", dose_);

		to_bson_array("r_sq", r_sq_, 2);
		to_bson_array("theta_sq", theta_sq_, 2);
		to_bson_array("r_theta", r_theta_, 2);

		return std::string((char*)doc.view().data(), doc.view().length());
	}

	bool  DoseModel::from_bson(const std::string& bson_str) {
		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_value_double("energy", energy_);
		from_bson_value_string("spot_tune_id", spot_tune_id_);
		from_bson_value_int64("ions_per_mu", ions_per_mu_);
		from_bson_value_double("idd_scale_factor", idd_scale_factor_);
		from_bson_value_double("bragg_peak_wel", bragg_peak_wel_);
		from_bson_value_double("energy_spread", energy_spread_);

		from_bson_vector_double("depth", depth_);
		from_bson_vector_double("dose", dose_);

		from_bson_array_double("r_sq", r_sq_, 2);
		from_bson_array_double("theta_sq", theta_sq_, 2);
		from_bson_array_double("r_theta", r_theta_, 2);
		return true;
	}
	//------------------------------------------------------------------------

	RangeShifter::RangeShifter() {

	}
	RangeShifter::~RangeShifter() {

	}
	std::shared_ptr<RangeShifter> RangeShifter::New() {
		return std::make_shared<RangeShifter>();

	}

	std::string RangeShifter::to_bson() {
		auto doc = document{};
		to_bson_value("name", name_);
		to_bson_value("mount_pos", mount_pos_);
		to_bson_value("plate_count", plate_count_);
		to_bson_value("material_name", material_name_);
		to_bson_value("ct_hu", ct_hu_);
		to_bson_value("radiation_length", radiation_length_);
		to_bson_value("acc_code", acc_code_);

		to_bson_vector("plate_thickness_list", plate_thickness_list_);
		to_bson_vector("plate_position_list", plate_position_list_);
		return std::string((char*)doc.view().data(), doc.view().length());
	}

	bool RangeShifter::from_bson(const std::string& bson_str) {
		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_value_string("name", name_);
		from_bson_value_string("mount_pos", mount_pos_);
		from_bson_value_int32("plate_count", plate_count_);
		from_bson_value_string("material_name", material_name_);
		from_bson_value_int32("ct_hu", ct_hu_);
		from_bson_value_double("radiation_length", radiation_length_);
		from_bson_value_string("acc_code", acc_code_);

		from_bson_vector_double("plate_thickness_list", plate_thickness_list_);
		from_bson_vector_double("plate_position_list", plate_position_list_);
		return true;

	}

	//------------------------------------------------------------------------

	Nozzle::Nozzle() {

	}
	Nozzle::~Nozzle() {

	}
	std::shared_ptr<Nozzle> Nozzle::New() {
		return std::make_shared<Nozzle>();

	}

	std::string Nozzle::to_bson() {
		auto doc = document{};
		to_bson_value("name", name_);
		to_bson_value("type", type_);
		to_bson_value("min_spot_mu", min_spot_mu_);
		to_bson_value("max_spot_mu", max_spot_mu_);
		to_bson_value("max_spot_count_per_beam", max_spot_count_per_beam_);
		to_bson_value("max_layer_count_per_beam", max_layer_count_per_beam_);
		to_bson_value("snout_tip_distance", snout_tip_distance_);

		to_bson_array("focal_length", focal_length_, 2);
		to_bson_array("couch_yaw_range", couch_yaw_range_, 2);
		to_bson_array("couch_roll_range", couch_roll_range_, 2);
		to_bson_array("couch_pitch_range", couch_pitch_range_, 2);
		to_bson_array("gantry_rot_range", gantry_rot_range_, 2);
		to_bson_array("snout_inner_diameter", snout_inner_diameter_, 2);
		to_bson_array("snout_outer_diameter", snout_outer_diameter_, 2);

		to_bson_obj_list("dose_models", dose_models_);
		to_bson_obj("range_shiter", range_shifter_);

		return std::string((char*)doc.view().data(), doc.view().length());

	}

	bool Nozzle::from_bson(const std::string& bson_str) {
		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_value_string("name", name_);
		from_bson_value_string("type", type_);
		from_bson_value_double("min_spot_mu", min_spot_mu_);
		from_bson_value_double("max_spot_mu", max_spot_mu_);
		from_bson_value_int32("max_spot_count_per_beam", max_spot_count_per_beam_);
		from_bson_value_int32("max_layer_count_per_beam", max_layer_count_per_beam_);
		from_bson_value_double("snout_tip_distance", snout_tip_distance_);

		from_bson_array_double("focal_length", focal_length_, 2);
		from_bson_array_double("couch_yaw_range", couch_yaw_range_, 2);
		from_bson_array_double("couch_roll_range", couch_roll_range_, 2);
		from_bson_array_double("couch_pitch_range", couch_pitch_range_, 2);
		from_bson_array_double("gantry_rot_range", gantry_rot_range_, 2);
		from_bson_array_double("snout_inner_diameter", snout_inner_diameter_, 2);
		from_bson_array_double("snout_outer_diameter", snout_outer_diameter_, 2);

		from_bson_obj_list("dose_models", dose_models_, DoseModel);
		from_bson_obj("range_shiter", range_shifter_, RangeShifter);

		return true;
	}

	bool Nozzle::to_file(const std::string& fn) {
		std::ofstream ofile(fn, std::ios::binary);
		if (!ofile.good())
			return false;
		auto& data = this->to_bson();

		ofile.write(data.c_str(), data.size());
		ofile.close();
		return true;
	}

	bool Nozzle::from_file(const std::string& fn) {

		std::ifstream infile(fn, std::ifstream::binary | std::ios::ate);
		if (infile.fail()) {
			return false;
		}
		std::string data;
		size_t size = infile.tellg();
		infile.seekg(0);
		data.resize(size);
		infile.read(&data[0], size);
		infile.close();

		return this->from_bson(data);
	}
	std::shared_ptr<DoseModel> Nozzle::get_dose_model(double energy) {
		for (auto dm : dose_models_) {
			if (abs(dm->get_energy() - energy) < 0.01) {
				return dm;
			}
		}
		return nullptr;
	}
	//------------------------------------------------------------------------
}
